from logging import raiseExceptions

from bcat.gtp.serde.de.reader import Reader
from bcat.gtp.serde.de.special_char import SpecialChar
import bcat.gtp.serde.error

#from bcat.gtp.serde.error import NeedDigitError, NeedAlphaError, ValueEmptyError, NewValueError, NormalByteAfterEscapeError, SpecialByteWithoutEscapeError, NeedCloseBracketsError, NeedOpenBracketsError, NeedCloseBracesError, NeedOpenBracesError, NeedCommaError
from bcat.gtp.serde.error import *


# gtp knowledge related
class Deserializer(object):
    def __init__(self, gtp_bytes):
        self.__reader = Reader(gtp_bytes)

    def __str__(self):
        return "Deserializer"

    def position(self):
        return self.__reader.position()

    def is_end(self):
        return self.__reader.is_end()

    def deser_pair_sep(self):
        current_byte = self.__reader.peek()
        if SpecialChar.is_pair_separator(current_byte):
            self.__reader.eat()
            return  True
        else:
            raise NeedCommaError(self.position())

    def deser_key_value_sep(self):
        current_byte = self.__reader.peek()
        if SpecialChar.is_kv_separator(current_byte):
            self.__reader.eat()
            return  True
        else:
            raise NeedEqualSignError(self.position())

    def deser_object_begin(self):
        current_byte = self.__reader.peek()
        if SpecialChar.is_object_begin(current_byte):
            self.__reader.eat()
            return  True
        else:
            raise NeedOpenBracesError(self.position())


    def deser_object_end(self):
        current_byte = self.__reader.peek()
        if SpecialChar.is_object_end(current_byte):
            self.__reader.eat()
            return
        else:
            raise NeedCloseBracesError(self.position())


    def deser_array_begin(self):
        current_byte = self.__reader.peek()
        if SpecialChar.is_array_begin(current_byte):
            self.__reader.eat()
            return  True
        else:
            raise NeedOpenBracketsError(self.position())


    def deser_array_end(self):
        current_byte = self.__reader.peek()
        if SpecialChar.is_array_end(current_byte):
            self.__reader.eat()
            return  True
        else:
            raise NeedCloseBracketsError(self.position())


    def deser_key(self):
        if self.__reader.available() < 3:
            raise NewValueError('when deser_key, need 3 bytes , but availabled length is {}' .format(self.__reader.available()))

        key_start = self.position()
        if SpecialChar.is_alpha(self.__reader.peek()):
            self.__reader.eat()
        else:
            # the first must be alpha
            raise NeedAlphaError(self.position())

        for i in range(2):
            if SpecialChar.is_digit(self.__reader.peek()):
                self.__reader.eat()
            else:
                # the second must be digit
                raise NeedDigitError(self.position())

        key_end = self.position()
        key = self.__reader.get(key_start, key_end)
        print("key : ", key)
        return key


    def deser_string(self):
        value_start = self.position()
        value_len = 0

        while not self.is_end():
            if SpecialChar.is_string_end(self.__reader.peek()):
                break

            if SpecialChar.is_slash(self.__reader.peek()):
                self.__reader.eat()
                value_len = value_len + 1

                if self.is_end():
                    #no letter after slash
                    raise NewValueError("No letter after slash")
                if SpecialChar.is_not_escape(self.__reader.peek()):
                    #must be eacaped letter after slash
                    raise NormalByteAfterEscapeError(self.position())
            else:
                if SpecialChar.is_escape(self.__reader.peek()):
                    # escape char without slash
                    raise SpecialByteWithoutEscapeError(self.position())


            self.__reader.eat()
            value_len = value_len + 1

        if value_len == 0:
            # empty string
            raise ValueEmptyError("offset:[{}], empty string" .format(self.position()))
        else:
            value_end = value_start + value_len
            value = self.__reader.get(value_start, value_end)
            print("value : ", value)
            return value


import unittest
class TestDeserializer(unittest.TestCase):
    def test_main(self):
        str_bytes = b'X03=A100,X06=L'
        deser = Deserializer(str_bytes)
        self.assertEqual(0, deser.position())
        self.assertFalse(deser.is_end())

        print(deser)
        while not deser.is_end():
            key = deser.deser_key()
            self.assertTrue(deser.deser_key_value_sep())
            value = deser.deser_string()
            if not deser.is_end():
                self.assertTrue(deser.deser_pair_sep())
            print('key : ', key, 'value : ', value)

    def test_deser_key_empty_value(self):
        str_bytes = b''
        deser = Deserializer(str_bytes)
        self.assertRaises(NewValueError, deser.deser_key)

    def test_deser_key_first_element_need_alpha(self):
        str_bytes = b'003=A100'
        deser = Deserializer(str_bytes)
        self.assertRaisesRegex(NeedAlphaError, "^offset:\\[0\\], need alpha:\\(a~z, A~Z\\)",deser.deser_key)

    def test_deser_key_sec_element_need_digit(self):
        str_bytes = b'XX3=A100'
        deser = Deserializer(str_bytes)
        self.assertRaisesRegex(NeedDigitError, "^offset:\\[1\\], need digit:\\(0~9\\)",deser.deser_key)

    def test_deser_key_third_element_need_digit(self):
        str_bytes = b'X0X=A100'
        deser = Deserializer(str_bytes)
        self.assertRaisesRegex(NeedDigitError, "^offset:\\[2\\], need digit:\\(0~9\\)",deser.deser_key)

    def test_deser_string_ValueEmptyError(self):
        str_bytes = b'X03='
        deser = Deserializer(str_bytes)

        key = deser.deser_key()
        deser.deser_key_value_sep()
        self.assertRaisesRegex(ValueEmptyError, "offset:\\[4\\], empty string", deser.deser_string)

    def test_deser_string_NormalCharAfterSlash(self):
        str_bytes = b'X03=\\a'
        deser = Deserializer(str_bytes)

        key = deser.deser_key()
        deser.deser_key_value_sep()
        self.assertRaises(NormalByteAfterEscapeError, deser.deser_string)

    def test_deser_string_SpecialCharWithoutSlash(self):
        str_bytes = b'X03=['
        deser = Deserializer(str_bytes)

        key = deser.deser_key()
        deser.deser_key_value_sep()
        self.assertRaises(SpecialByteWithoutEscapeError, deser.deser_string)

    def test_deser_string_with_string_end_char_colon(self):
        str_bytes = b'X03=,'
        deser = Deserializer(str_bytes)

        key = deser.deser_key()
        deser.deser_key_value_sep()
        self.assertRaisesRegex(ValueEmptyError, "offset:\\[4\\], empty string", deser.deser_string)

    def test_deser_string_with_string_end_char_close_brackets(self):
        str_bytes = b'X03=]'
        deser = Deserializer(str_bytes)

        key = deser.deser_key()
        deser.deser_key_value_sep()
        self.assertRaisesRegex(ValueEmptyError, "offset:\\[4\\], empty string", deser.deser_string)

    def test_deser_string_with_string_end_char_close_braces(self):
        str_bytes = b'X03=}'
        deser = Deserializer(str_bytes)

        key = deser.deser_key()
        deser.deser_key_value_sep()
        self.assertRaisesRegex(ValueEmptyError, "offset:\\[4\\], empty string", deser.deser_string)
